﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

namespace Nirvana
{

    [CustomPropertyDrawer(typeof(ModelAnimationImportConfig))]
    public sealed class ModelAnimationImportConfigPropertyDrawer : PropertyDrawer
    {
        private int index = 0;

        public override void OnGUI(Rect pos, SerializedProperty property, GUIContent label)
        {
            EditorGUI.BeginProperty(pos, label, property);

           // EditorGUI.PrefixLabel(pos, GUIUtility.GetControlID(FocusType.Passive), label);
            EditorGUI.indentLevel++;

            SerializedProperty animationType = property.FindPropertyRelative("animationType");
            SerializedProperty optimizeGameObjects = property.FindPropertyRelative("optimizeGameObjects");
            SerializedProperty importAnimation = property.FindPropertyRelative("importAnimation");
            SerializedProperty bakeAnimations = property.FindPropertyRelative("bakeAnimations");
            SerializedProperty resampleCurves = property.FindPropertyRelative("resampleCurves");
            SerializedProperty animationCompression = property.FindPropertyRelative("animationCompression");
            SerializedProperty animationRotationError = property.FindPropertyRelative("animationRotationError");
            SerializedProperty animationPositionError = property.FindPropertyRelative("animationPositionError");
            SerializedProperty animationScaleError = property.FindPropertyRelative("animationScaleError");
       
            SerializedProperty humanoidOversampling = property.FindPropertyRelative("humanoidOversampling");
            SerializedProperty motionNodeName = property.FindPropertyRelative("motionNodeName");
            //SerializedProperty generateAnimations = property.FindPropertyRelative("generateAnimations");
            //SerializedProperty animationWrapMode = property.FindPropertyRelative("animationWrapMode");

            SerializedProperty autoSplitAnimationClip = property.FindPropertyRelative("autoSplitAnimationClip");
            SerializedProperty floatOptimize = property.FindPropertyRelative("floatOptimize");
            SerializedProperty clearEmptyCurve = property.FindPropertyRelative("clearEmptyCurve");
            
            SerializedProperty scaleOptimize = property.FindPropertyRelative("scaleOptimize");

            index = 0;
            DrawAnimationInfo(pos);
            DrawAniamtionTypeGUI(pos, animationType);
            DrawAniamtionCompressGUI(pos, animationCompression);
            DrawAniamtionCompressErrorsGUI(pos, animationRotationError, animationPositionError, animationScaleError);
            DrawAniamtionClipOptimizeGUI(pos, floatOptimize, scaleOptimize, clearEmptyCurve);
            DrawAniamtionSplitGUI(pos, autoSplitAnimationClip);

            int offsetY = 5;
            index = index + 3;
            var r1 = new Rect(pos.x, pos.y + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight*2);
            index++;
            var r2 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);

            offsetY = offsetY + 2;
            var r111 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r222 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);



            offsetY = offsetY + 2;
            var r3 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r4 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r5 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 5;
            var r6 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r7 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight*2);
            offsetY = offsetY + 5;
            index++;
            var r8 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r9 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 5;
            var r10 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r11 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 2;
            var r12 = new Rect(pos.x, pos.y + offsetY + index++ * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            offsetY = offsetY + 8;

            EditorGUI.LabelField(r1, new GUIContent("优化游戏对象，开启此项，可以优化有动画的角色的性能,这个优化会大幅节省Animatior.Update这一项的耗时。\n骨骼里有挂载节点的话，手动开启去暴露挂载点，不要在这里勾选自动"));
            EditorGUI.PropertyField(r2, optimizeGameObjects);


#if UNITY_2019_OR_NEW
            SerializedProperty avatarDefinition = property.FindPropertyRelative("avatarDefinition");
            EditorGUI.LabelField(r111, new GUIContent("选择获取 Avatar 定义的位置,静态物体则设置None"));
            EditorGUI.PropertyField(r222, avatarDefinition);
#endif



            EditorGUI.LabelField(r3, new GUIContent("从此资源导入动画,静态Mesh就不要开启导入动画功能"));
            EditorGUI.PropertyField(r4, importAnimation);
            EditorGUI.LabelField(r5, new GUIContent("烘焙通过反向动力学(IK)"));
            EditorGUI.PropertyField(r6, bakeAnimations);

            EditorGUI.LabelField(r7, new GUIContent("将动画曲线重新采样为四元数值，并为动画中的每个帧生成一个新的四元数关键帧。\n默认情况下会启用此选项。仅当原始动画中的关键点之间存在插值问题时，才应禁用此选项来保留最初创作的动画曲线。"));
            EditorGUI.PropertyField(r8, resampleCurves);

            EditorGUI.LabelField(r9, new GUIContent("控制在导入Human动画以进行重定向时，使用多少倍采样率"));
            EditorGUI.PropertyField(r10, humanoidOversampling);

            EditorGUI.LabelField(r11, new GUIContent("用于生成动画运动的变换组件的路径，路径应如下所示：“Reference/Root”"));
            EditorGUI.PropertyField(r12, motionNodeName);

            EditorGUI.indentLevel--;

            EditorGUI.EndProperty();

        }

        /// <summary>
        /// 绘制所有属性字段在GUI上所需的整体长度
        /// </summary>
        /// <param name="property"></param>
        /// <param name="label"></param>
        /// <returns></returns>
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return 100 * EditorGUIUtility.singleLineHeight;
        }

        private void DrawAnimationInfo(Rect pos)
        {
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y + 5 + (++index) * EditorGUIUtility.singleLineHeight, pos.width, 4 * EditorGUIUtility.singleLineHeight);
            index = index + 4;
            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight * 8);
            index = index + 8;

            var r5 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r6 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight+5, pos.width, EditorGUIUtility.singleLineHeight * 8);
            index = index + 8;

            EditorGUI.LabelField(r1, "Animator组件数据信息", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "具体选中Animator组件查看\n" +
            "Clip Count：7\n" +
            "Curves Pos:399 ,Quat:399, Euler:0, Scale:399, Muscles:0, Generic:0, PPtr:0\n" +
            "Curves Count:3990, Constant:2338(58.6%), Dense:0(0.0%), Stream:1652(41.4%)\n", MessageType.Info);
            
            EditorGUI.HelpBox(r4,
                "Clip Count:动画剪辑总数\n" +
                "Curves(Pos,Rot&Scale): 由Unity用于动画化游戏对象位置、旋转或缩放的曲线总数,这些曲线用于不属于标准人形骨架的动画游戏对象.在对人形Avatar进行动画化时，这些曲线会显示额外非肌肉骨骼（例如武器，尾巴、飘逸的布料或悬垂的吊坠）的计数。如果您有人形动画并发现意外的非肌肉动画曲线，表示动画文件中可能有不必要的动画曲线。\n" +
                "Constant: 代表基本不变的数据，哪些数据用这个存储方式？很典型的就是scale曲线，基本上都是1，Constant的内存占用很小，基本上只有关键帧值数据，不含有切线数据\n" +
                "Dense: 采用的是线性插值方式，不含有切线数据，少内存。一般是线性插值的曲线会采用这种方式\n" +
                "Stream: 采用的是曲线插值方式，含有切线，时间等全部数据，与Dense方法相比，此数据占用的内存多得多\n", MessageType.None
                );

            EditorGUI.LabelField(r5, "优化思路", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r6,
            "基本原则就是增加Constant数据，减少Stream数据。\n\n" +
            "1，float存储精度降低: 这个方法不仅会减少文件大小，而且还会减少内存。精度降低了，数据存储就会转变成Constant方式。默认存储每一帧transform信息的是10位精度的float格式数据，缩减此数据为3位精度float，视觉效果基本一样。可以Ctrl+D,从模型中把动画复制出来，通过文本查看。\n\n" +
            "2, 剔除常量的变化曲线 (变化小的关键帧)， 比较曲线的输入和输出变化，如果是基本稳定，即为常量。\n" +
            "3, 压缩关键帧: unity导入压缩 Keyframe Reduction,Rotation Error: 0.15,Position Error: 0.15,Scale Error: 1 \n" +
            "4, 去除动画文件的scale信息: 美术作的人物动画，是不需要用到scale的，所以动画数据里有大量的scale冗余信息，体现在constant数目过多，需要去掉scale信息\n", MessageType.None);
        }
    
        private void DrawAniamtionTypeGUI(Rect pos,SerializedProperty animationType)
        {
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y  + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y +5+ (++index) * EditorGUIUtility.singleLineHeight, pos.width, 7*EditorGUIUtility.singleLineHeight);
            index = index + 7;
            int board = 4;
            var wrap = new Rect(r2.x - board, r2.y - board, pos.width + 2 * board, (index - startIndex) * EditorGUIUtility.singleLineHeight + 2 * board);

            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);

            EditorGUI.DrawRect(wrap, new Color(0.25f, 0.25f, 0.25f, 1));
            EditorGUI.LabelField(r1, "动画类型", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "None：无\n" +
            "Legacy：传统的Animation\n" +
            "Generic：一般Mecanim动画(包含武器等动画)\n" +
            "Humanoid：人形Mecanim动画(单独人物动画，不能包含武器等动画)\n" +
            "\nUnity默认的AnimationType是Generic，对包含动画数据的FBX文件是适用这个选项的。但是如果FBX文件中没有包含动画数据，而只是普通静态Mesh的话，设置为Generic会导致引擎自动为这个Mesh的GameObject添加一个Animator组件，导致不必要的消耗。因此对于不包含动画数据的模型文件，建议把这个选项设置为None", MessageType.None);
            GUI.color = Color.yellow;
            EditorGUI.PropertyField(r4, animationType);
            GUI.color = Color.white;
        }

        private void DrawAniamtionCompressGUI(Rect pos, SerializedProperty animationCompress)
        {
            index = index + 1;
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y + 5 + (++index) * EditorGUIUtility.singleLineHeight, pos.width, 4 * EditorGUIUtility.singleLineHeight);
            index = index + 4;
            int board = 4;
            var wrap = new Rect(r2.x - board, r2.y - board, pos.width + 2 * board, (index - startIndex) * EditorGUIUtility.singleLineHeight + 2 * board);

            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);

            EditorGUI.DrawRect(wrap, new Color(0.25f, 0.25f, 0.25f, 1));
            EditorGUI.LabelField(r1, "动画压缩类型", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "Off：不压缩\n" +
            "KeyframeReduction：减少关键帧，移除相似的关键帧,这会影响文件大小（运行时内存）以及如何计算曲线。仅适用于 Generic 动画类型\n" +
            "KeyframeReduction and Compression：减少关键帧并压缩Clip数据,这仅影响文件大小；运行时内存大小与 Keyframe Reduction 相同,仅适用于 Legacy 动画类型\n" +
            "Optimal：最优化，(GenericorHumanoid)。官方会选择最优的压缩方式来进行压缩，建议选择这个,仅适用于 Generic 和 Humanoid 动画类型\n", MessageType.None);
            GUI.color = Color.yellow;
            EditorGUI.PropertyField(r4, animationCompress);
            GUI.color = Color.white;
        }

        private void DrawAniamtionClipOptimizeGUI(Rect pos, SerializedProperty floatOptimize, SerializedProperty scaleOptimize, SerializedProperty clearEmptyCurve)
        {
            index = index + 2;
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y + 5 + (++index) * EditorGUIUtility.singleLineHeight, pos.width, 5 * EditorGUIUtility.singleLineHeight);
            index = index + 5;



            //index = index + 2;
            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r5 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight + 5, pos.width, EditorGUIUtility.singleLineHeight);
            var r6 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight + 10, pos.width, EditorGUIUtility.singleLineHeight);

            int board = 4;
            var wrap = new Rect(r2.x - board, r2.y - board, pos.width + 2 * board, (index - startIndex - 1) * EditorGUIUtility.singleLineHeight + 2 * board + 10);

            EditorGUI.DrawRect(wrap, new Color(0.25f, 0.25f, 0.25f, 1));
            EditorGUI.LabelField(r1, "动画曲线优化", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "1.缩短float类型的精度，导致动画文件内点的位置发生了变化，引起Constant Curve和Dense Curve的数量也有可能发生变化，\n" +
            "最终可能导致动画的点更稀疏，而连续相同的点更多了。所以Dense Curve是减少了，Constant Curve是增多了，总的内存是减小了\n\n" +
            "2.剔除常量的变化曲线 (变化小的关键帧)， 比较曲线的输入和输出变化，如果是基本稳定，即为常量。\n" +
            "3.对于一般的人形动画需求，不会有模型骨骼scale变化的情况。因此我们可以把动画信息的scale部分去除，可以节约一部分大小\n", MessageType.None);
            GUI.color = Color.yellow;
            EditorGUI.PropertyField(r4, floatOptimize);
            EditorGUI.PropertyField(r5, clearEmptyCurve);
            EditorGUI.PropertyField(r6, scaleOptimize); 
            GUI.color = Color.white;
        }

        /// <summary>
        /// 绘制动画容错度UI
        /// </summary>
        private void DrawAniamtionCompressErrorsGUI(Rect pos, SerializedProperty rotaionError, SerializedProperty positionError, SerializedProperty scaleError)
        {
            index = index + 1;
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y + 5 + (++index) * EditorGUIUtility.singleLineHeight, pos.width, 5 * EditorGUIUtility.singleLineHeight);
            index = index + 5;


          
            //index = index + 2;
            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r5 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight+5, pos.width, EditorGUIUtility.singleLineHeight);
            var r6 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight+10, pos.width, EditorGUIUtility.singleLineHeight);

            int board = 4;
            var wrap = new Rect(r2.x - board, r2.y - board, pos.width + 2 * board, (index - startIndex-1) * EditorGUIUtility.singleLineHeight + 2 * board+10);

            EditorGUI.DrawRect(wrap, new Color(0.25f, 0.25f, 0.25f, 1));
            EditorGUI.LabelField(r1, "动画压缩容错度", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "设置容错度以减少位置曲线和缩放曲线上的关键点,从而达到增大Constant,减少Stream目的\n" +
            "Rotation Error: 设置旋转曲线压缩的容错度(以度为单位的角度),小于此旋转角度的关键点，都会删除\n\n" +
            "Position Error: 设置位置曲线压缩的容错度(百分比0-100)\n\n" +
            "Scale Error: 设置缩放曲线压缩的容错度(百分比0-100)", MessageType.Info);
            GUI.color = Color.yellow;
            EditorGUI.PropertyField(r4, rotaionError);
            EditorGUI.PropertyField(r5, positionError);
            EditorGUI.PropertyField(r6, scaleError);
            GUI.color = Color.white;
        }

        private void DrawAniamtionSplitGUI(Rect pos, SerializedProperty animationType)
        {
            index = index + 2;
            int startIndex = index;
            var r1 = new Rect(pos.x, pos.y + ++index * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);
            var r2 = new Rect(pos.x, pos.y + 5 + (++index) * EditorGUIUtility.singleLineHeight, pos.width, 5 * EditorGUIUtility.singleLineHeight);
            index = index + 5;
            int board = 4;
            var wrap = new Rect(r2.x - board, r2.y - board, pos.width + 2 * board, (index - startIndex) * EditorGUIUtility.singleLineHeight + 2 * board);

            var r4 = new Rect(pos.x, pos.y + (++index) * EditorGUIUtility.singleLineHeight, pos.width, EditorGUIUtility.singleLineHeight);

            EditorGUI.DrawRect(wrap, new Color(0.25f, 0.25f, 0.25f, 1));
            EditorGUI.LabelField(r1, "动画切割", GUITheme.LabelTitleStyle);
            EditorGUI.HelpBox(r2,
            "有时美术提供一个未切割的总体动画，此时我们需要在同目录下创建一个和FBX同名的切割文件 fbxname.split ,里面设置好每个切割动画的参数， 脚本会根据split文件对每个动画作切割。\n\n" +
            "文件格式说明：\n" +
            "每个动画的参数格式： clipname firstframe lastframe loop\n" +
            "例如：待机动画 ： idle 0 70 true\n", MessageType.None);
            GUI.color = Color.yellow;
            EditorGUI.PropertyField(r4, animationType);
            GUI.color = Color.white;
        }

    }
}

